using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

using VoxelSystem.VoxelSystem;

namespace VoxelSystem
{
    public class TestGame : Microsoft.Xna.Framework.Game
    {
        #region Default TestGame Members
        private GraphicsDeviceManager _graphics;
        private SpriteBatch _spriteBatch;
        private GraphicsDevice _device;
        private Effect _effect;

        private Matrix _viewMatrix;
        private Matrix _projectionMatrix;

        private Vector3 _cameraFocus = new Vector3(0, 0, 0);
        private Vector3 _cameraFocusAim = new Vector3(0, 0, 0);
        private float _cameraRadius = 1.0f;
        private float _cameraAngle = 0.0f;
        private float _cameraSpeed_move = 1.0f;
        private float _cameraSpeed_rotate = 1.0f;
        private float _cameraSmooth = 25;
        #endregion

        #region Members
        private VS_VoxelSystem _voxelSystem;
        private VertexDeclaration _vertexDeclaration;

        private Texture2D _crosshair;
        #endregion

        public TestGame()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        #region Overridden Engine Methods
        protected override void Initialize()
        {
            _graphics.PreferredBackBufferWidth = 800;
            _graphics.PreferredBackBufferHeight = 600;
            _graphics.IsFullScreen = false;
            _graphics.ApplyChanges();

            _viewMatrix = Matrix.CreateLookAt(-Vector3.UnitZ, Vector3.Zero, Vector3.Up);
            _projectionMatrix = Matrix.CreateOrthographicOffCenter(0, (float)GraphicsDevice.Viewport.Width, (float)GraphicsDevice.Viewport.Height, 0, VS_Settings.GridDistance/10, VS_Settings.GridDistance*1000);

            _voxelSystem = new VS_VoxelSystem(this);

            base.Initialize();
        }
        protected override void LoadContent()
        {
            _device = _graphics.GraphicsDevice;
            _spriteBatch = new SpriteBatch(GraphicsDevice);

            _effect = Content.Load<Effect>("effects");
            updateViewMatrix();
            _projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, _device.Viewport.AspectRatio, VS_Settings.GridDistance/10, VS_Settings.GridDistance*1000);

            _vertexDeclaration = new VertexDeclaration(_device, VertexPositionColor.VertexElements);


            // test
            _crosshair = Content.Load<Texture2D>("crosshair");


        }
        protected override void UnloadContent() {}
        protected override void Update(GameTime gameTime)
        {
            float timeDiff = (float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f;
            processInput(timeDiff);

            _voxelSystem.Update(gameTime);

            base.Update(gameTime);
        }
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(new Color(0.2f, 0.191f, 0.185f));

            //GraphicsDevice.Clear(Color.Beige);
            
            


            _device.VertexDeclaration = _vertexDeclaration;

            _effect.CurrentTechnique = _effect.Techniques["Colored"];
            _effect.Parameters["xView"].SetValue(_viewMatrix);
            _effect.Parameters["xProjection"].SetValue(_projectionMatrix);
            _effect.Parameters["xWorld"].SetValue(Matrix.Identity);

            _effect.Begin();
            foreach (EffectPass pass in _effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                
                _voxelSystem.Draw(gameTime);
                pass.End();
            }
            _effect.End();

            _spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

            // (width/2) - (imgwidth/2) 
            int x = (_device.Viewport.Width / 2) - (_crosshair.Width / 2);
            int y = (_device.Viewport.Height / 2) - (_crosshair.Height / 2);
            int w = 100;
            int h = 100;
            _spriteBatch.Draw(_crosshair, new Rectangle(x,y,w,h), Color.White);

            _spriteBatch.End();

            base.Draw(gameTime);
        }
        #endregion

        #region Methods
        private void updateViewMatrix()
        {
            if (Math.Abs(_cameraFocus.X - _cameraFocusAim.X) > 0.001f || Math.Abs((_cameraFocus.Y - _cameraFocusAim.Y)) > 0.001f || Math.Abs((_cameraFocus.Z - _cameraFocusAim.Z)) > 0.001f)
            {
                _cameraFocus.X -= (_cameraFocus.X - _cameraFocusAim.X) / _cameraSmooth;
                _cameraFocus.Y -= (_cameraFocus.Y - _cameraFocusAim.Y) / _cameraSmooth;
                _cameraFocus.Z -= (_cameraFocus.Z - _cameraFocusAim.Z) / _cameraSmooth;
            }

            Vector3 position = new Vector3((float)Math.Sin(_cameraAngle) * _cameraRadius, 0, (float)Math.Cos(_cameraAngle) * _cameraRadius);
            _viewMatrix = Matrix.CreateLookAt(position + _cameraFocus, _cameraFocus, Vector3.Up);
        }
        private void processInput(float amount)
        {
            KeyboardState keyState = Keyboard.GetState();
            if (keyState.IsKeyDown(Keys.Up))
                _cameraRadius += _cameraSpeed_move * amount;
            if (keyState.IsKeyDown(Keys.Down))
                _cameraRadius -= _cameraSpeed_move * amount;
            if (keyState.IsKeyDown(Keys.Right))
                _cameraAngle += _cameraSpeed_rotate * amount;
            if (keyState.IsKeyDown(Keys.Left))
                _cameraAngle -= _cameraSpeed_rotate * amount;

            Vector3 oldCameraFocus = new Vector3(_cameraFocus.X, _cameraFocus.Y, _cameraFocus.Z);
            if (keyState.IsKeyDown(Keys.NumPad8))
                oldCameraFocus.Z += _cameraSpeed_move * amount;
            if (keyState.IsKeyDown(Keys.NumPad2))
                oldCameraFocus.Z -= _cameraSpeed_move * amount;
            if (keyState.IsKeyDown(Keys.NumPad6))
                oldCameraFocus.X += _cameraSpeed_move * amount;
            if (keyState.IsKeyDown(Keys.NumPad4))
                oldCameraFocus.X -= _cameraSpeed_move * amount;
            if (keyState.IsKeyDown(Keys.NumPad9))
                oldCameraFocus.Y += _cameraSpeed_move * amount;
            if (keyState.IsKeyDown(Keys.NumPad3))
                oldCameraFocus.Y -= _cameraSpeed_move * amount;
            _cameraFocus = oldCameraFocus;

            if (keyState.IsKeyDown(Keys.Escape))
                this.Exit();

            updateViewMatrix();

            _voxelSystem.ProcessInput(keyState, amount);
        }
        #endregion

        #region Default TestGame Properties
        public Matrix ViewMatrix
        {
            get { return _viewMatrix; }
            set { _viewMatrix = value; }
        }
        public Matrix ProjectionMatrix
        {
            get { return _projectionMatrix; }
            set { _projectionMatrix = value; }
        }
        public Effect EffectDefault
        {
            get { return _effect; }
        }
        public GraphicsDevice Device
        {
            get { return _device; }
        }
        public GraphicsDeviceManager Graphics
        {
            get { return _graphics; }
        }
        public SpriteBatch SpriteBatch
        {
            get { return _spriteBatch; }
        }
        #endregion

        #region Properties
        public VS_VoxelSystem VoxelSystem
        {
            get { return _voxelSystem; }
        }
        public Vector3 CameraTarget
        {
            get { return _cameraFocus; }
            set { _cameraFocusAim = value; }
        }
        #endregion
    }
    
    // entry point
    static class Program
    {
        static void Main(string[] args)
        {
            using (TestGame game = new TestGame())
            {
                game.Run();
            }
        }
    }
}